import type { HttpRequestConfig, HttpError } from "./type";
import { formatToken, sessionKey, setToken, type DataInfo } from "@/utils/auth";
import { http } from "./index";
import router from "@/router/index";
import { useUserStore } from "@/stores/modules/user";
import { WHITELIST } from "./constants";
import { delay } from "lodash-unified";
import { storageSession } from "@pureadmin/utils";

/** token 过期后，暂存待执行的请求 */
let requests = [];

/** 标志正在刷新 token，防止重复刷新 token */
let isRefreshing = false;

/** 缓存原始请求 */
const retryOriginalRequest = (config: HttpRequestConfig) => {
  return new Promise((resolve) => {
    requests.push((token: string) => {
      config.headers["Cup-Auth"] = formatToken(token);
      resolve(http.request(config));
    });
  });
};

/** 刷新 token 后, 将缓存的接口重新请求一次 */
function onAccessTokenFetched(newToken: string) {
  requests.forEach((callback) => {
    callback(newToken);
  });
  // 清空缓存接口
  requests = [];
}

export const senselessRefresh = <T extends HttpError>(
  axiosError: T,
  tipFn: (error: T) => void
): Promise<any> => {
  const errorRes = axiosError.response;
  const reqOption = axiosError.config;
  if (WHITELIST.some((u) => errorRes.config.url.includes(u))) {
    // 接口白名单的请求不需要 token， 所以也就不需要刷新token，防止死循环
    tipFn(axiosError);
    return Promise.reject(axiosError);
  } else {
    return new Promise<any>((resolve, reject) => {
      const currentPath = router.currentRoute.value?.path ?? "/";
      /** 2. 开始刷新 token 流程 */
      if (currentPath !== "/login") {
        const data = storageSession().getItem<DataInfo<number>>(sessionKey);
        if (!isRefreshing) {
          /** 3. 正在刷新 token 操作，后续接口请求不会走以下流程 */
          isRefreshing = true;
          useUserStore()
            .handRefreshToken(data.refresh_token)
            .then((res) => {
              const newToken = formatToken(res.access_token);
              setToken(res);
              /** 使用新 token 重新调用之前的失效请求 */
              onAccessTokenFetched(newToken);
            })
            .catch((err) => {
              console.log("❌ 刷新token失败, 退出重新登录", err);
              tipFn(axiosError);
              delay(useUserStore().logOut, 500);
            })
            .finally(() => {
              // 标志刷新完成
              isRefreshing = false;
            });
        }
        /** 1. 添加失效请求到缓存数组 */
        resolve(retryOriginalRequest(reqOption));
      } else {
        tipFn(axiosError);
        reject(axiosError);
      }
    });
  }
};
